#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import os

from app.clients.ones_api_client import OnesAPIClient
from typing import Dict, List, Optional, Any
import json
from datetime import datetime
from app.core.database import SessionLocal
from app.models.test_models import TestLibrary, TestModule, TestCase, TestStep, PriorityEnum, CaseTypeEnum
from sqlalchemy.orm import Session
import logging

logger = logging.getLogger(__name__)

class OnesSyncService:
    """ONES用例同步服务"""
    
    def __init__(self):
        self.ones_client = OnesAPIClient()
        self.target_library_name = "NVR测试"
        
        # ONES字段映射 - 将ONES内部UUID映射到我们的枚举值
        self.priority_mapping = {
            # 这些是示例映射，需要根据实际ONES API返回的值进行调整
            "PRIOPThh": PriorityEnum.HIGHEST.value,
            "PRIOPThi": PriorityEnum.HIGH.value, 
            "PRIOPTno": PriorityEnum.NORMAL.value,
            "PRIOPTlo": PriorityEnum.LOW.value,
            "PRIOPTll": PriorityEnum.LOWEST.value,
            # 如果是UUID格式的，添加默认映射
        }
        
        self.case_type_mapping = {
            "functional": CaseTypeEnum.FUNCTIONAL.value,
            "performance": CaseTypeEnum.PERFORMANCE.value,
            "api": CaseTypeEnum.API.value,
            "install": CaseTypeEnum.INSTALL.value,
            "config": CaseTypeEnum.CONFIG.value,
            "safety": CaseTypeEnum.SAFETY.value,
            "other": CaseTypeEnum.OTHER.value,
        }
    
    def _map_priority(self, ones_priority: str) -> str:
        """
        映射ONES优先级到我们的枚举值
        
        Args:
            ones_priority: ONES系统中的优先级值
            
        Returns:
            str: 映射后的优先级枚举值
        """
        if not ones_priority:
            return PriorityEnum.NORMAL.value
            
        # 直接匹配已知的优先级值
        if ones_priority in self.priority_mapping:
            return self.priority_mapping[ones_priority]
            
        # 如果是UUID格式（8个字符的随机字符串），使用默认值
        if len(ones_priority) == 8 and ones_priority.isalnum():
            logger.warning(f"未知的优先级UUID: {ones_priority}，使用默认值NORMAL")
            return PriorityEnum.NORMAL.value
            
        # 其他情况也使用默认值
        logger.warning(f"未知的优先级值: {ones_priority}，使用默认值NORMAL")
        return PriorityEnum.NORMAL.value
    
    def _map_case_type(self, ones_type: str) -> str:
        """
        映射ONES用例类型到我们的枚举值
        
        Args:
            ones_type: ONES系统中的用例类型值
            
        Returns:
            str: 映射后的用例类型枚举值
        """
        if not ones_type:
            return CaseTypeEnum.FUNCTIONAL.value
            
        # 直接匹配已知的类型值
        if ones_type in self.case_type_mapping:
            return self.case_type_mapping[ones_type]
            
        # 如果是UUID格式，使用默认值
        if len(ones_type) == 8 and ones_type.isalnum():
            logger.warning(f"未知的用例类型UUID: {ones_type}，使用默认值functional")
            return CaseTypeEnum.FUNCTIONAL.value
            
        # 其他情况也使用默认值
        logger.warning(f"未知的用例类型值: {ones_type}，使用默认值functional")
        return CaseTypeEnum.FUNCTIONAL.value
    
    def _map_assign_user(self, ones_assign: str) -> Optional[str]:
        """
        映射ONES负责人到我们的用户名
        
        Args:
            ones_assign: ONES系统中的负责人UUID
            
        Returns:
            Optional[str]: 映射后的用户名，如果是UUID则返回None
        """
        if not ones_assign:
            return None
            
        # 如果是UUID格式（8个字符的随机字符串），返回None让数据库字段为空
        if len(ones_assign) == 8 and ones_assign.isalnum():
            logger.debug(f"负责人为UUID格式: {ones_assign}，设置为空")
            return None
            
        # 如果是可读的用户名，直接返回
        return ones_assign
        
    def sync_nvr_library(self) -> Dict[str, Any]:
        """
        同步NVR测试用例库
        
        Returns:
            Dict[str, Any]: 同步结果统计
        """
        logger.info("开始同步NVR测试用例库")
        
        try:
            # 1. 查找NVR测试用例库
            library = self.ones_client.find_library_by_name(self.target_library_name)
            if not library:
                return {"error": f"未找到用例库: {self.target_library_name}"}
            
            logger.info(f"找到用例库: {library['name']} (UUID: {library['uuid']})")
            
            # 2. 获取用例库中的所有用例 - 使用新的fallback方法
            logger.info("开始获取用例库中的测试用例...")
            success, cases_data = self.ones_client.get_library_cases_with_fallback(library["uuid"])
            if not success:
                logger.error(f"获取用例失败: {cases_data}")
                return {"error": f"获取用例失败: {cases_data}"}
            
            cases = cases_data.get("cases", [])
            modules = library.get("modules", [])
            
            logger.info(f"成功获取到 {len(cases)} 个测试用例, {len(modules)} 个模块")
            
            # 如果仍然没有用例，记录详细信息
            if len(cases) == 0:
                logger.warning("⚠️ 未获取到任何测试用例，可能的原因：")
                logger.warning("1. 用例库为空")
                logger.warning("2. API权限不足")
                logger.warning("3. API端点或格式有变化")
                logger.warning(f"用例库UUID: {library['uuid']}")
                logger.warning(f"模块数量: {len(modules)}")
                
                # 仍然继续同步用例库和模块信息
                cases = []
            
            # 3. 保存到本地数据库
            with SessionLocal() as db:
                sync_result = self._sync_to_database(db, library, modules, cases)
            
            # 添加用例数量信息到结果中
            sync_result["total_cases_from_ones"] = len(cases)
            sync_result["total_modules_from_ones"] = len(modules)
            
            logger.info("NVR测试用例库同步完成")
            return sync_result
            
        except Exception as e:
            logger.error(f"同步NVR测试用例库失败: {str(e)}", exc_info=True)
            return {"error": str(e)}
    
    def _sync_to_database(self, db: Session, library: Dict, modules: List[Dict], cases: List[Dict]) -> Dict[str, Any]:
        """
        将数据同步到本地数据库
        
        Args:
            db: 数据库会话
            library: 用例库信息
            modules: 模块列表
            cases: 用例列表
            
        Returns:
            Dict[str, Any]: 同步统计结果
        """
        stats = {
            "library_created": False,
            "library_updated": False,
            "modules_created": 0,
            "modules_updated": 0,
            "cases_created": 0,
            "cases_updated": 0,
            "errors": []
        }
        
        try:
            # 1. 同步用例库
            db_library = db.query(TestLibrary).filter(
                TestLibrary.ones_uuid == library["uuid"]
            ).first()
            
            if not db_library:
                # 创建新用例库
                db_library = TestLibrary(
                    name=library["name"],
                    ones_uuid=library["uuid"],
                    description=f"从ONES同步的{library['name']}用例库",
                    ones_create_time=datetime.fromtimestamp(library.get("create_time", 0)),
                    last_sync_time=datetime.now()
                )
                db.add(db_library)
                db.flush()  # 获取ID
                stats["library_created"] = True
                logger.info(f"创建用例库: {library['name']}")
            else:
                # 更新用例库
                db_library.last_sync_time = datetime.now()
                stats["library_updated"] = True
                logger.info(f"更新用例库: {library['name']}")
            
            # 2. 同步模块
            module_map = {}  # ones_uuid -> db_module
            
            # 创建模块层次结构映射
            ones_modules_map = {mod["uuid"]: mod for mod in modules}
            
            # 先处理顶级模块，再处理子模块
            def process_module(module_data: Dict, parent_id: Optional[int] = None):
                db_module = db.query(TestModule).filter(
                    TestModule.ones_uuid == module_data["uuid"]
                ).first()
                
                if not db_module:
                    # 创建新模块
                    db_module = TestModule(
                        name=module_data["name"],
                        ones_uuid=module_data["uuid"],
                        library_id=db_library.id,
                        parent_id=parent_id,
                        path=module_data.get("path", ""),
                        ones_create_time=datetime.fromtimestamp(module_data.get("create_time", 0)),
                        position=module_data.get("position", 0)
                    )
                    db.add(db_module)
                    db.flush()
                    stats["modules_created"] += 1
                    logger.debug(f"创建模块: {module_data['name']}")
                else:
                    # 更新模块
                    db_module.name = module_data["name"]
                    db_module.path = module_data.get("path", "")
                    db_module.position = module_data.get("position", 0)
                    db_module.parent_id = parent_id
                    stats["modules_updated"] += 1
                    logger.debug(f"更新模块: {module_data['name']}")
                
                module_map[module_data["uuid"]] = db_module
                return db_module
            
            # 先处理顶级模块
            for module in modules:
                if not module.get("parent_uuid"):
                    process_module(module)
            
            # 再处理子模块
            for module in modules:
                if module.get("parent_uuid"):
                    parent_module = module_map.get(module["parent_uuid"])
                    parent_id = parent_module.id if parent_module else None
                    process_module(module, parent_id)
            
            # 3. 同步测试用例
            for case in cases:
                try:
                    self._sync_test_case(db, case, db_library.id, module_map, stats)
                except Exception as e:
                    error_msg = f"同步用例失败 {case.get('name', 'Unknown')}: {str(e)}"
                    logger.error(error_msg)
                    stats["errors"].append(error_msg)
            
            # 4. 提交事务
            db.commit()
            
            logger.info(f"同步完成 - 库:{stats['library_created'] or stats['library_updated']} "
                       f"模块:+{stats['modules_created']}~{stats['modules_updated']} "
                       f"用例:+{stats['cases_created']}~{stats['cases_updated']}")
            
            return stats
            
        except Exception as e:
            db.rollback()
            logger.error(f"数据库同步失败: {str(e)}")
            stats["errors"].append(str(e))
            return stats
    
    def _sync_test_case(self, db: Session, case: Dict, library_id: int, 
                       module_map: Dict[str, Any], stats: Dict[str, Any]):
        """
        同步单个测试用例
        
        Args:
            db: 数据库会话
            case: 用例数据
            library_id: 用例库ID
            module_map: 模块映射
            stats: 统计数据
        """
        # 查找是否已存在
        db_case = db.query(TestCase).filter(
            TestCase.ones_uuid == case["uuid"]
        ).first()
        
        # 获取模块ID
        module_id = None
        if case.get("module_uuid") and case["module_uuid"] in module_map:
            module_id = module_map[case["module_uuid"]].id
        
        # 映射ONES字段到我们的模型字段
        mapped_priority = self._map_priority(case.get("priority"))
        mapped_case_type = self._map_case_type(case.get("type"))
        mapped_assign_user = self._map_assign_user(case.get("assign"))
        
        if not db_case:
            # 创建新用例
            db_case = TestCase(
                name=case["name"],
                ones_uuid=case["uuid"],
                library_id=library_id,
                module_id=module_id,
                priority=mapped_priority,
                case_type=mapped_case_type,
                assign_user=mapped_assign_user,
                description=case.get("desc", ""),
                precondition=case.get("condition", ""),
                ones_create_time=datetime.fromtimestamp(case.get("create_time", 0)) if case.get("create_time") else None,
                status="active"
            )
            db.add(db_case)
            db.flush()
            stats["cases_created"] += 1
            logger.debug(f"创建用例: {case['name']}")
        else:
            # 更新用例
            db_case.name = case["name"]
            db_case.module_id = module_id
            db_case.priority = mapped_priority
            db_case.case_type = mapped_case_type
            db_case.assign_user = mapped_assign_user
            db_case.description = case.get("desc", "")
            db_case.precondition = case.get("condition", "")
            stats["cases_updated"] += 1
            logger.debug(f"更新用例: {case['name']}")
        
        # 同步测试步骤
        self._sync_test_steps(db, case, db_case.id)
    
    def _sync_test_steps(self, db: Session, case: Dict, case_id: int):
        """
        同步测试用例的步骤
        
        Args:
            db: 数据库会话
            case: 用例数据
            case_id: 用例ID
        """
        # 删除现有步骤
        db.query(TestStep).filter(TestStep.case_id == case_id).delete()
        
        # 添加新步骤
        steps = case.get("steps", [])
        for i, step in enumerate(steps):
            db_step = TestStep(
                case_id=case_id,
                step_number=i + 1,
                description=step.get("desc", ""),
                expected_result=step.get("result", ""),
                ones_uuid=step.get("uuid")
            )
            db.add(db_step)
    
    def get_sync_status(self) -> Dict[str, Any]:
        """
        获取同步状态
        
        Returns:
            Dict[str, Any]: 同步状态信息
        """
        try:
            with SessionLocal() as db:
                # 查找NVR测试用例库
                library = db.query(TestLibrary).filter(
                    TestLibrary.name == self.target_library_name
                ).first()
                
                if not library:
                    return {
                        "synced": False,
                        "message": "NVR测试用例库尚未同步"
                    }
                
                # 统计信息
                modules_count = db.query(TestModule).filter(
                    TestModule.library_id == library.id
                ).count()
                
                cases_count = db.query(TestCase).filter(
                    TestCase.library_id == library.id
                ).count()
                
                return {
                    "synced": True,
                    "library_name": library.name,
                    "last_sync_time": library.last_sync_time.isoformat() if library.last_sync_time else None,
                    "modules_count": modules_count,
                    "cases_count": cases_count,
                    "ones_uuid": library.ones_uuid
                }
                
        except Exception as e:
            logger.error(f"获取同步状态失败: {str(e)}")
            return {
                "error": str(e)
            }
    
    def force_resync(self) -> Dict[str, Any]:
        """
        强制重新同步
        
        Returns:
            Dict[str, Any]: 同步结果
        """
        logger.info("开始强制重新同步NVR测试用例库")
        
        try:
            with SessionLocal() as db:
                # 删除现有的NVR测试相关数据
                library = db.query(TestLibrary).filter(
                    TestLibrary.name == self.target_library_name
                ).first()
                
                if library:
                    # 删除相关数据
                    db.query(TestStep).filter(
                        TestStep.case_id.in_(
                            db.query(TestCase.id).filter(TestCase.library_id == library.id)
                        )
                    ).delete(synchronize_session=False)
                    
                    db.query(TestCase).filter(TestCase.library_id == library.id).delete()
                    db.query(TestModule).filter(TestModule.library_id == library.id).delete()
                    db.delete(library)
                    db.commit()
                    
                    logger.info("已删除现有NVR测试数据")
            
            # 重新同步
            return self.sync_nvr_library()
            
        except Exception as e:
            logger.error(f"强制重新同步失败: {str(e)}")
            return {"error": str(e)}
    
    def sync_custom_library(self, library_name: str) -> Dict[str, Any]:
        """
        同步指定的用例库
        
        Args:
            library_name: 要同步的用例库名称
        
        Returns:
            Dict[str, Any]: 同步结果统计
        """
        logger.info(f"开始同步用例库: {library_name}")
        
        try:
            # 1. 查找指定的用例库
            library = self.ones_client.find_library_by_name(library_name)
            if not library:
                return {"error": f"未找到用例库: {library_name}"}
            
            logger.info(f"找到用例库: {library['name']} (UUID: {library['uuid']})")
            
            # 2. 获取用例库中的所有用例
            logger.info("开始获取用例库中的测试用例...")
            success, cases_data = self.ones_client.get_library_cases_with_fallback(library["uuid"])
            if not success:
                logger.error(f"获取用例失败: {cases_data}")
                return {"error": f"获取用例失败: {cases_data}"}
            
            cases = cases_data.get("cases", [])
            modules = library.get("modules", [])
            
            logger.info(f"成功获取到 {len(cases)} 个测试用例, {len(modules)} 个模块")
            
            # 如果仍然没有用例，记录详细信息
            if len(cases) == 0:
                logger.warning("⚠️ 未获取到任何测试用例，可能的原因：")
                logger.warning("1. 用例库为空")
                logger.warning("2. API权限不足")
                logger.warning("3. API端点或格式有变化")
                logger.warning(f"用例库UUID: {library['uuid']}")
                logger.warning(f"模块数量: {len(modules)}")
                
                # 仍然继续同步用例库和模块信息
                cases = []
            
            # 3. 保存到本地数据库
            with SessionLocal() as db:
                sync_result = self._sync_to_database(db, library, modules, cases)
            
            # 添加用例数量信息到结果中
            sync_result["total_cases_from_ones"] = len(cases)
            sync_result["total_modules_from_ones"] = len(modules)
            sync_result["library_name"] = library_name
            
            logger.info(f"用例库 {library_name} 同步完成")
            return sync_result
            
        except Exception as e:
            logger.error(f"同步用例库 {library_name} 失败: {str(e)}", exc_info=True)
            return {"error": str(e)}
    
    def get_available_libraries(self) -> Dict[str, Any]:
        """
        获取ONES中可用的用例库列表
        
        Returns:
            Dict[str, Any]: 用例库列表
        """
        logger.info("获取ONES中的用例库列表")
        
        try:
            success, data = self.ones_client.get_libraries()
            if not success:
                logger.error(f"获取用例库列表失败: {data}")
                return {"error": f"获取用例库列表失败: {data}"}
            
            libraries = data.get("libraries", [])
            
            # 格式化用例库信息
            library_list = []
            for lib in libraries:
                library_info = {
                    "uuid": lib.get("uuid"),
                    "name": lib.get("name"),
                    "create_time": lib.get("create_time"),
                    "modules_count": len(lib.get("modules", [])),
                    "is_synced": False  # 后续检查是否已同步
                }
                library_list.append(library_info)
            
            # 检查哪些用例库已经同步到本地
            with SessionLocal() as db:
                for lib_info in library_list:
                    local_lib = db.query(TestLibrary).filter(
                        TestLibrary.ones_uuid == lib_info["uuid"]
                    ).first()
                    if local_lib:
                        lib_info["is_synced"] = True
                        lib_info["local_id"] = local_lib.id
                        lib_info["last_sync_time"] = local_lib.last_sync_time.isoformat() if local_lib.last_sync_time else None
            
            return {
                "total": len(library_list),
                "libraries": library_list
            }
            
        except Exception as e:
            logger.error(f"获取用例库列表失败: {str(e)}", exc_info=True)
            return {"error": str(e)}
    
    def force_resync_custom(self, library_name: str) -> Dict[str, Any]:
        """
        强制重新同步指定用例库
        
        Args:
            library_name: 要重新同步的用例库名称
        
        Returns:
            Dict[str, Any]: 同步结果
        """
        logger.info(f"开始强制重新同步用例库: {library_name}")
        
        try:
            with SessionLocal() as db:
                # 查找现有的用例库
                library = db.query(TestLibrary).filter(
                    TestLibrary.name == library_name
                ).first()
                
                if library:
                    # 删除相关数据
                    logger.info(f"删除现有用例库数据: {library_name}")
                    
                    # 删除测试步骤
                    db.query(TestStep).filter(
                        TestStep.case_id.in_(
                            db.query(TestCase.id).filter(TestCase.library_id == library.id)
                        )
                    ).delete(synchronize_session=False)
                    
                    # 删除测试用例
                    db.query(TestCase).filter(TestCase.library_id == library.id).delete()
                    
                    # 删除模块
                    db.query(TestModule).filter(TestModule.library_id == library.id).delete()
                    
                    # 删除用例库
                    db.delete(library)
                    db.commit()
                    
                    logger.info(f"已删除现有 {library_name} 用例库数据")
            
            # 重新同步
            return self.sync_custom_library(library_name)
            
        except Exception as e:
            logger.error(f"强制重新同步用例库 {library_name} 失败: {str(e)}", exc_info=True)
            return {"error": str(e)}


def main():
    """测试同步功能"""
    service = OnesSyncService()
    
    print("🔄 开始同步NVR测试用例库")
    
    # 1. 检查同步状态
    status = service.get_sync_status()
    print(f"当前状态: {status}")
    
    # 2. 执行同步
    if status.get("synced"):
        print("用例库已存在，执行增量同步...")
        result = service.sync_nvr_library()
    else:
        print("首次同步，创建新的用例库...")
        result = service.sync_nvr_library()
    
    print(f"同步结果: {result}")
    
    # 3. 检查最终状态
    final_status = service.get_sync_status()
    print(f"最终状态: {final_status}")


if __name__ == "__main__":
    main() 